package helper.scxml.scxml2.t0.case2_traffic.zone

import helper.base.A2LHM
import helper.base.BaseTypeHelper.LHMGetExpand.getMaxKey
import helper.base.LHMHelper.A2LHMExpand.add
import helper.base.LHMHelper.A2LHMExpand.makeA2LHM
import helper.base.debug.DebugHelper.DebuggerList
import helper.base.debug.DebugHelper.DebuggerList.Companion.makeDebuggerList
import helper.base.debug.DebugRes.d_ZoneLearningTest_t2t1
import helper.base.debug.DebugRes.d_t1t1Calculate
import helper.base.debug.DebugRes.d_t_2023_0828_143709_
import helper.base.math.DoubleMatrixHelper.DoubleMatrix
import helper.scxml.scxml2.MathHelper.LocationEventVListLHM
import helper.scxml.scxml2.t0.case2_traffic.zone.TrafficEnvHelper.CalObj.D2DFun.Companion.makeD2DFun
import helper.scxml.scxml2.t0.case2_traffic.zone.TrafficEnvHelper.StrategyObj.ER.aUniformERStrategy
import helper.scxml.scxml2.t0.case2_traffic.zone.TrafficEnvHelper.StrategyObj.Ren.aUniformRenStrategy
import helper.scxml.scxml2.t0.case2_traffic.zone.TrafficEnvHelper.StrategyObj.Ren.getUniformCs
import helper.scxml.scxml2.t0.case2_traffic.zone.TrafficEnvHelper.StrategyObj.getSimpleT1EnvStrategy
import helper.scxml.scxml2.t0.case2_traffic.zone.TrafficEnvHelper.TrafficEnv.Companion.makeTrafficEnv
import helper.scxml.scxml2.t0.case2_traffic.zone.TrafficEnvHelper.TrafficStrategyEx
import helper.scxml.scxml2.t0.case2_traffic.zone.TrafficLearningTest.MyLearningHelper.Companion.aGetTotalScoreFun
import helper.scxml.scxml2.t0.case2_traffic.zone.TrafficLearningTest.MyLearningHelper.Companion.makeMyLearningHelper
import helper.scxml.scxml2.zone.t2.ConfigHelper.Config
import helper.scxml.scxml2.zone.t2.ZoneEnvHelper.ZoneEnv
import helper.scxml.scxml2.zone.t2.ZoneLearningHelper.GetDFun
import helper.scxml.scxml2.zone.t2.ZoneLearningHelper.GetDFun.Companion.aGetDistanceToCovarianceMatrix
import helper.scxml.scxml2.zone.t2.ZoneLearningHelper.GetDFun.Companion.aGetDistanceToMean
import helper.scxml.scxml2.zone.t2.ZoneLearningHelper.IDsDFun
import helper.scxml.scxml2.zone.t2.ZoneLearningHelper.LearningHelper
import helper.scxml.strategy.StrategyHelper.TimedEventHelper.ITimedEvent
import helper.scxml.strategy.StrategyHelper.TimedEventHelper.RangeTimedEvent.Companion.makeRangeTimedEvent
import helper.scxml.strategy.TotalStrategyHelper.LeafSNode.Companion.makeLeafSNode
import helper.scxml.strategy.TotalStrategyHelper.SNode
import helper.scxml.strategy.sNodeUnit.SNodeUnitHelper.ISNodeUnit
import helper.scxml.strategy.sNodeUnit.T2SNodeUnitHelper.ERT2SNodeUnit.Companion.makeL0ERT2SNodeUnit
import helper.scxml.strategy.sNodeUnit.T2SNodeUnitHelper.SubT2SNodeUnit
import helper.scxml.strategy.sNodeUnit.T2SNodeUnitHelper.T2SNodeUnit
import org.junit.Test

internal class TrafficLearningTest {
    @Test
    fun t0() {
        val debuggerList = makeDebuggerList()
        val env = makeTrafficEnv()
        val t2SNodeUnit = makeL0ERT2SNodeUnit(
            env = getSimpleT1EnvStrategy(),
            ren = aUniformRenStrategy,
        )
        val cal = env.aT1Calculate(
            t2SNodeUnit,
            debuggerList,
        )
        println(cal)
    }
    //{t=344.1032434542115, r=0.032256416790588854}
    //{t=343.63138623593073, r=0.032256416790588854}

    class MyLearningHelper : LearningHelper() {
        companion object {
            fun makeMyLearningHelper(
                maxRuns_repeatNum: Int = 0,
                maxGood_repeatNum: Int = 0,
                maxBest_heapSize: Int = 0,
                maxNoBetter: Int = 0,
                maxIterations: Int = 0,
                maxResets: Int = Int.MAX_VALUE,
                getDFun: GetDFun,
                clearHeapP: Double,
                ifToTxt: Boolean,
                doDoubleMatrixFun: DoubleMatrix.() -> DoubleMatrix,
                aDsDFun: IDsDFun,
            ): MyLearningHelper {
                return MyLearningHelper().also {
                    it.maxRuns_repeatNum = maxRuns_repeatNum
                    it.maxGood_repeatNum = maxGood_repeatNum
                    it.maxBest_heapSize = maxBest_heapSize
                    it.maxNoBetter = maxNoBetter
                    it.maxIterations = maxIterations
                    it.maxResets = maxResets
                    it.getDFun = getDFun
                    it.clearHeapP = clearHeapP
                    it.ifToTxt = ifToTxt
                    it.doDoubleMatrixFun = doDoubleMatrixFun
                    it.aDsDFun = aDsDFun
                }
            }

            val aGetTotalScoreFun = { aDsDFun: IDsDFun, cal: A2LHM<String, Double> ->
                aDsDFun.aDsToString(
                    cal["t"]!!,
                    cal["r"]!!,
                )
            }
        }

        override fun getTotalScore(
            cal: A2LHM<String, Double>,
        ): Double {
            return aGetTotalScoreFun(aDsDFun, cal)
        }

        override val aEnvLabel = "2_traffic"

        override val aGetEnvFun = {
            makeTrafficEnv()
        }

        override fun getEnvStrategyCal(
            aEnv: ZoneEnv,
            aStrategy: ISNodeUnit,
            debuggerList: DebuggerList,
        ): A2LHM<String, Double> {
            return aEnv.aT1Calculate(
                aStrategy,
                debuggerList,
            )
        }

        override fun byWeightSelectChoice(
            env: ZoneEnv,
            ren: SubT2SNodeUnit,
            locationName: String,
            locationEventVListLHM: LocationEventVListLHM,
            zs: Config,
            aSNode: SNode,
            nowTString: String,
            nowDoubleT: Double,
        ) {
            val choiceWeightLHM = makeA2LHM<ITimedEvent, Double>()

            //
            val xIntRange = 0..env.stateStayMaxLHM[locationName]!!

            fun t300(nowDoubleT: Double) {
                val newT = nowDoubleT
                val newV = arrayListOf(newT)
                locationEventVListLHM[locationName]?.forEach { (event, v) ->
                    val weight = v.getWeightOf(newV, getDFun.getDFun)
                    choiceWeightLHM.add(
                        makeRangeTimedEvent(
                            xIntRange,
                            event,
                        ),
                        weight,
                    )
                }
            }
            //
            t300(nowDoubleT)
            //
            val leafSNode = makeLeafSNode()
            val maxKey: ITimedEvent? = choiceWeightLHM.getMaxKey()
            if (maxKey != null) {
                leafSNode.eventDPLHM.add(maxKey, 1.0)
            } else {
                getUniformCs(locationName).forEach {
                    leafSNode.eventDPLHM.add(it.key, it.value)
                }
            }
            ren.relationLHM.add(aSNode, nowTString, leafSNode)
        }

        fun aT1ByLearningMakeStrategy(
            aDirStr: String = aBaseDirStr,
            getUStrategyScoreFun: () -> Double = { Double.MAX_VALUE },
            debuggerList: DebuggerList = makeDebuggerList(),
        ): T2SNodeUnit {
            return fByLearningMakeStrategy(
                aDirStr,
                TrafficStrategyEx,
                { env, locationEventVListLHM, aStrategyEx ->
                    val aT2SNodeUnit = aStrategyEx.makeNewStrategy()
                    val ren = aT2SNodeUnit.stringSubT2SNodeUnitLHM["ren"]!!
                    arrayOf("s0", "s7").forEach { locationName ->
                        doWhenEnvRenLocation(
                            env,
                            ren,
                            locationName,
                            locationEventVListLHM,
                        )
                    }
                    aT2SNodeUnit
                },
                getUStrategyScoreFun = getUStrategyScoreFun,
                debuggerList = debuggerList,
            )
        }

        fun repeatT1ByLearningMakeStrategy(
            repeatTimes: Int,
            aBaseDirStr: String = this.aBaseDirStr,
            shouldDrawChart: Boolean = true,
            debuggerList: DebuggerList = makeDebuggerList(),
        ) {
            fRepeatT1ByLearningMakeStrategy(
                repeatTimes,
                { aDirStr ->
                    aT1ByLearningMakeStrategy(
                        aDirStr,
                        debuggerList = debuggerList,
                    )
                },
                aBaseDirStr,
                shouldDrawChart,
                debuggerList,
            )
        }

        override fun aISNodeStringFun(aStrategy: ISNodeUnit): String {
            return aStrategy.toL2String()
        }
    }

    val aDsDFun = makeD2DFun { t, r ->
        t + r / 100.0
    }

    @Test
    fun t2_debug() {
        val t2ArgUnit = makeMyLearningHelper(
            maxRuns_repeatNum = 11,
            maxGood_repeatNum = 10,
            maxBest_heapSize = 200,
            maxNoBetter = 100,
            maxIterations = 200,
            getDFun = aGetDistanceToMean,
            clearHeapP = 1.0,
            ifToTxt = true,
            doDoubleMatrixFun = {
                this
            },
            aDsDFun = aDsDFun,
        )
        t2ArgUnit.aT1ByLearningMakeStrategy(
            debuggerList = makeDebuggerList(
                d_t1t1Calculate,
                d_ZoneLearningTest_t2t1,
            ),
        )
    }

    fun aGetUStrategyScoreFun(): Double {
        val env = makeTrafficEnv()
        val cal = env.aT0Calculate(aUniformERStrategy, repeatTimes = 1000)
        println(cal)
        val totalScore = aGetTotalScoreFun(aDsDFun, cal)
        println("bestTotalScore=${totalScore}")
        return totalScore
    }

    @Test
    fun test_aGetUStrategyScoreFun() {
        aGetUStrategyScoreFun()
    }

    @Test
    fun t2t1() {
        val t2ArgUnit = makeMyLearningHelper(
            maxRuns_repeatNum = 11,
            maxGood_repeatNum = 10,
            maxBest_heapSize = 200,
            maxNoBetter = 100,
            maxIterations = 20000,
            getDFun = aGetDistanceToMean,
            clearHeapP = 1.0,
            ifToTxt = true,
            doDoubleMatrixFun = {
                this
            },
            aDsDFun = aDsDFun,
        )
        t2ArgUnit.aT1ByLearningMakeStrategy(
            getUStrategyScoreFun = {
                aGetUStrategyScoreFun()
            },
            debuggerList = makeDebuggerList(
                d_t1t1Calculate,
                d_ZoneLearningTest_t2t1,
            ),
        )
    }
    //41.95454545454552

    @Test
    fun t2t2() {
        val t2ArgUnit = makeMyLearningHelper(
            maxRuns_repeatNum = 11,
            maxGood_repeatNum = 10,
            maxBest_heapSize = 200,
            maxNoBetter = 100,
            maxIterations = 20000,
            getDFun = aGetDistanceToCovarianceMatrix,
            clearHeapP = 1.0,
            ifToTxt = true,
            doDoubleMatrixFun = {
                for (i in 0 until this.rows) {
                    this[i, i] += 0.01
                }
                this
            },
            aDsDFun = aDsDFun,
        )
        t2ArgUnit.aT1ByLearningMakeStrategy(
            getUStrategyScoreFun = {
                aGetUStrategyScoreFun()
            },
            debuggerList = makeDebuggerList(
                d_t1t1Calculate,
                d_ZoneLearningTest_t2t1,
                d_t_2023_0828_143709_,
            ),
        )
    }
    //41.95454545454552

    @Test
    fun repeat_t2t1() {
        val t2ArgUnit = makeMyLearningHelper(
            maxRuns_repeatNum = 11,
            maxGood_repeatNum = 10,
            maxBest_heapSize = 200,
            maxNoBetter = 10,
            maxIterations = 4000,
            maxResets = 1000,
            getDFun = aGetDistanceToMean,
            clearHeapP = 1.0,
            ifToTxt = true,
            doDoubleMatrixFun = {
                this
            },
            aDsDFun = makeD2DFun { t, r ->
                t + r / 100.0
            },
        )
        t2ArgUnit.repeatT1ByLearningMakeStrategy(
            repeatTimes = 10,
            debuggerList = makeDebuggerList(
                d_t1t1Calculate,
                d_ZoneLearningTest_t2t1,
            ),
        )
    }

    @Test
    fun repeat_t2t2() {
        val t2ArgUnit = makeMyLearningHelper(
            maxRuns_repeatNum = 11,
            maxGood_repeatNum = 10,
            maxBest_heapSize = 200,
            maxNoBetter = 100,
            maxIterations = 20000,
            maxResets = 1000,
            getDFun = aGetDistanceToCovarianceMatrix,
            clearHeapP = 1.0,
            ifToTxt = true,
            doDoubleMatrixFun = {
                for (i in 0 until this.rows) {
                    this[i, i] += 0.01
                }
                this
            },
            aDsDFun = makeD2DFun { t, r ->
                t + r / 100.0
            },
        )
        t2ArgUnit.repeatT1ByLearningMakeStrategy(
            repeatTimes = 10,
            debuggerList = makeDebuggerList(
                d_t1t1Calculate,
                d_ZoneLearningTest_t2t1,
                d_t_2023_0828_143709_,
            ),
        )
    }
}